package com.jule.teenpatti.game.network.protocol.reqs;

import JoloProtobuf.GameSvr.JoloGame;
import com.jule.teenpatti.base.enums.ErrorCodeEnum;
import com.jule.teenpatti.base.enums.TableStateEnum;
import com.jule.teenpatti.game.model.eenum.NextBetPlayerReasonEnum;
import com.jule.teenpatti.game.play.TableUtil;
import com.jule.teenpatti.log.service.LogReasons;
import com.jule.teenpatti.game.config.Config;
import com.jule.teenpatti.game.dao.bean.CommonConfigModel;
import com.jule.teenpatti.game.gameUtil.GameLogic;
import com.jule.teenpatti.game.model.PlayerInfo;
import com.jule.teenpatti.game.model.eenum.PlayTypeEnum;
import com.jule.teenpatti.game.model.eenum.PlayerStateEnum;
import com.jule.teenpatti.game.network.protocol.ClientReq;
import com.jule.teenpatti.game.network.protocol.acks.JoloGame_ApplyBetAck_50005;
import com.jule.teenpatti.game.play.AbstractTable;
import com.jule.teenpatti.game.service.TableService;
import com.jule.teenpatti.game.utils.log.TableLogUtil;
import com.jule.teenpatti.game.vavle.notice.NoticeBroadcastMessages;
import io.netty.buffer.ByteBuf;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 申请下注
 */
public class JoloGame_ApplyBetReq_50005 extends ClientReq {

    private final static Logger logger = LoggerFactory.getLogger(JoloGame_ApplyBetReq_50005.class);

    public JoloGame_ApplyBetReq_50005(int functionId) {
        super(functionId);
    }

    private JoloGame.JoloGame_ApplyBetReq req;

    @Override
    public void readPayLoadImpl(ByteBuf buf) throws Exception {
        byte[] blob = new byte[buf.readableBytes()];
        buf.readBytes(blob);
        req = JoloGame.JoloGame_ApplyBetReq.parseFrom(blob);
        this.setTable(TableService.getInstance().getTable(header.gameId + "", req.getRoomId(), req.getTableId()));
    }

    @Override
    public void processImpl() throws Exception {
        long start = System.currentTimeMillis();
        logger.debug("收到消息-> " + functionId + ", reqNum-> " + header.reqNum + ", req->" + req.toString());
        String userId = req.getUserId();
        String roomId = req.getRoomId();
        String tableId = req.getTableId();
        String gameOrderId = req.getGameOrderId();
        long betScore = req.getBetScore();
        int betRoundId = req.getBetRoundId();
        AbstractTable table = getTable();
        JoloGame.JoloGame_ApplyBetAck.Builder ack = JoloGame.JoloGame_ApplyBetAck.newBuilder();
        try {
            ack.setUserId(userId);
            ack.setRoomId(roomId);
            ack.setTableId(tableId);
            ack.setGameOrderId(gameOrderId);
            ack.setBetScore(betScore);
            ack.setBetRoundId(betRoundId);

            PlayerInfo player = table.getPlayer(userId);
            if (null == player) {
                logger.info("can't found player info, playerId->" + userId + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResult(-1).setResultMsg(ErrorCodeEnum.GAME_50050_2.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            if (player.getSeatNum() <= 0) {
                logger.info("player have no seat,can't bet. playerId->" + userId + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResult(-2).setResultMsg(ErrorCodeEnum.GAME_50014_1.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            if (player.getState().getValue() != PlayerStateEnum.beting.getValue()) {
                logger.info("player state invalid,can't bet. playerId->" + userId + ", state->" + player.getState() + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResult(-3).setResultMsg(ErrorCodeEnum.GAME_50013_1.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            int currRoundId = table.getCurrRoundId();
            String currGameOrderId = table.getCurrGameOrderId();
            double baseBetScore = table.getCurrBaseBetScore();

            //如果gameOrderId不一致，那么返回错误
            if (!gameOrderId.equals(currGameOrderId)) {
                ack.setResult(-13);
                logger.error("GameOrderId不一致, req gameOrderId->" + gameOrderId + ", currGameOrderId->" + currGameOrderId + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50013_2.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            //如果下注轮次小于当前下注轮次，说明消息错误，申请的下注轮次已经过期
            if (betRoundId < currRoundId) {
                ack.setResult(-4);
                logger.error("betRoundId error, req betRoundId->" + betRoundId + ", game currRoundId->" + currRoundId + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50012_2.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            //如果是盲牌玩家，基础下注额减半
            PlayerInfo playerInfo = TableUtil.getPreBetPlayer(table,player.getSeatNum());
            if (player.getIsBlind() == 1 && playerInfo != null) {
                baseBetScore = baseBetScore / 2;
            }

            //非庄家/状态在游戏中/下注数量小于桌面带入/小于等于最大值/大于等于最小值/下注值是最小下注值的倍数
            if (betScore > player.getPlayScoreStore()) {
                ack.setResult(-5);
                logger.error("Player desktop score is not enough" + " -> " +
                        "desktopScore:" + player.getPlayScoreStore() + " -> betScore:" + betScore + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50001_4.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            if (betScore < baseBetScore) {
                ack.setResult(-6);
                logger.info("betScore less than minimum, minBet->{}, betScore->{}, ,tableInfo->{}", baseBetScore, betScore, TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50005_1.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            if (player.getIsBlind() == 1 && betScore > table.getRoomConfig().getMaxBetScore() / 2) {
                ack.setResult(-7);
                logger.info("blind betScore greater than maximum, maxBet->{}, betScore->{}, tableInfo->{}",
                        table.getRoomConfig().getMaxBetScore() / 2, betScore, TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50005_4.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

            if (betScore > table.getRoomConfig().getMaxBetScore()) {
                ack.setResult(-8);
                logger.error("betScore greater than maximum -> " +
                        "maxBet:" + table.getRoomConfig().getMaxBetScore() + " -> betScore:" + betScore + ",tableInfo:" + TableUtil.toStringNormal(table));
                ack.setResultMsg(ErrorCodeEnum.GAME_50005_2.getCode());
                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
                return;
            }

//            if (betScore % baseBetScore != 0) {
//                ack.setResult(-9);
//                logger.error("betScore is not the multiplier of the amount of baseBetScore-> " +
//                        "baseBetScore:" + baseBetScore + " -> betScore:" + betScore + ",tableInfo:" + TableUtil.toStringNormal(table));
//                ack.setResultMsg(ErrorCodeEnum.GAME_50005_3.getCode());
//                ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
//                return;
//            }

            ack.setUserId(userId);
            ack.setGameOrderId(gameOrderId);
            ack.setResult(1);
            ack.setBetRoundId(currRoundId);
            ack.setBetScore(betScore);

            table.setTotalAlreadyBet(table.getTotalAlreadyBet() + betScore); //桌内累计下注金额
            player.minusPlayScoreStore(betScore, LogReasons.CommonLogReason.GAME_BET);
            player.setCurChipsLose(player.getCurChipsLose() + betScore);
            ack.setCurrPlayScore(player.getPlayScoreStore());
            ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));

            //发送通知
            NoticeBroadcastMessages.moneyChangeNotice(table,userId,player.getPlayScoreStore());

            table.setLastActionTime(System.currentTimeMillis());

            TableLogUtil.bet(functionId, "bet", userId, player.getNickName(),
                    table.getPlayType() + "", table.getRoomId(), table.getTableId(), table.getCurrGameOrderId(),
                    betRoundId, table.getCurrRoundId(), betScore, player.getPlayScoreStore());

            player.setState(PlayerStateEnum.already_bet);//修改玩家状态值为已下注
            player.setAlreadyBetScore4Round(betScore); //修改玩家下注值
            player.setTotalAlreadyBetScore4Hand(player.getTotalAlreadyBetScore4Hand() + betScore);//累计玩家总得下注值
            table.accumulativeBet(player.getPlayerId(), betScore);

            int betSeqId = table.checkAndReturnBetSeqId();
            PlayerInfo nextActionPlayer = table.getNextBetPlayer(player.getSeatNum(), NextBetPlayerReasonEnum.GAME_LOGIC_BET);

            //修改牌桌当前最小下注值
            long currBaseBetScore = betScore;
            if(currBaseBetScore % table.getRoomConfig().getAnte() == 0) {
                if (player.getIsBlind() == 1) {
                    currBaseBetScore = betScore * 2;
                }
                table.setCurrBaseBetScore(currBaseBetScore);
            }

            if (nextActionPlayer == null || table.getTotalAlreadyBet() >= table.getMaxPotScore()) {
                CommonConfigModel commonConfig = table.getCommonConfig();
                logger.debug("当前结算类型："+(commonConfig.getAutoSettleType() == 1 ? "轮次上线" : "底池上线"));
//                if (commonConfig.getAutoSettleType() == 2) { //自动结算类型：1,底池上限 2,轮次上线
//                    //广播提示玩家总分超过桌子上线
//                    NoticeBroadcastMessages.settlePrompt(table);
//                    //调用定时器来延迟结算时间
//                    GameLogic.commonTimer(table, 2, false);
//                    nextActionPlayer = null; //进入结算则将下一个行动者置空
//                } else { //此处为极端情况容错
//                    //调用定时器来延迟结算时间
//                    GameLogic.commonTimer(table, 2, false);
//                }
                //轮次达到上限 从庄家的下家开始比牌
                PlayerInfo playerA = table.getShowPlayer(table.getCurrDealerSeatNum());
                PlayerInfo targetPlayer = table.getShowPlayer(playerA.getSeatNum());
                NoticeBroadcastMessages.settlePrompt(table);
                //设置桌子状态为比牌中
                table.setTableStateEnum(TableStateEnum.OPEN_CARD);
                GameLogic.specialShowTimer(table, 1, true,playerA,targetPlayer);
            } else {
                // show牌条件：桌面只剩两人且都看牌 或者 blind的玩法只剩两人且达到可show牌轮次
                if (table.getInGamePlayers().size() == 2
                        && table.getSeeCardNum() == 2) {
                    nextActionPlayer.setIsCanShow(1);
                }
                if (table.getInGamePlayers().size() == 2 && table.currPlayType() == PlayTypeEnum.blind.getGameId()
                        && table.getCurrRoundId() > Config.BLIND_SHOW_ROUNDCOUNT) {
                    nextActionPlayer.setIsCanShow(1);
                }
                nextActionPlayer.setState(PlayerStateEnum.beting);
                //启动下一个行动玩家的bet倒计时
                GameLogic.betTimer(table);
            }

            //广播玩家下注
            try {
                NoticeBroadcastMessages.betRoundDoBet(table, betSeqId, player, nextActionPlayer,true);
            } catch (Exception ex) {
                logger.error("SendNotice ERROR：", ex);
            }
        } catch (Exception ex) {
            ack.setResult(-10).setResultMsg(ErrorCodeEnum.GAME_50002_2.getCode());
            ctx.writeAndFlush(new JoloGame_ApplyBetAck_50005(ack.build(), header));
            logger.error("", ex);
        } finally {
            logger.debug("Bet ACK info: " + ack.toString());
            logger.debug("Bet ACK bytes length: " + ack.build().toByteArray().length);
            if (null != table) {
                logger.debug("All Player info: " + System.getProperty("line.separator") + TableUtil.toStringAllPlayers(table));

                logger.debug("InGame Player info: " + System.getProperty("line.separator") + TableUtil.toStringInGamePlayers(table));
            }
            logger.debug("Bet over. Table state: " + table.getTableStateEnum());

            long useTimes = System.currentTimeMillis() - start;
            if(useTimes > 500) {
                logger.error("ApplyBet。 耗时->{} 毫秒, reqNum->{}", useTimes, header.reqNum); //TODO:临时增加
            }
        }
    }
}
